package com.springboot.frame.starter.csrf;

import org.springframework.util.Assert;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * csrf过滤器
 *
 * @author madman
 */
public final class CsrfFilter extends OncePerRequestFilter {

  private final CsrfTokenRepository tokenRepository;

  private final CsrfMatcher csrfMatcher;

  private final CsrfProcessor csrfProcessor;

  public CsrfFilter() {
    this.tokenRepository = new SessionCsrfTokenRepository();
    this.csrfMatcher = new MethodCsrfMatcher();
    this.csrfProcessor = new DefaultCsrfProcessor();
  }

  public CsrfFilter(CsrfTokenRepository csrfTokenRepository, CsrfMatcher csrfMatcher,
      CsrfProcessor csrfProcessor) {
    Assert.notNull(csrfTokenRepository, "csrfTokenRepository cannot be null");
    Assert.notNull(csrfMatcher, "csrfMatcher cannot be null");
    Assert.notNull(csrfProcessor, "csrfProcessor cannot be null");
    this.tokenRepository = csrfTokenRepository;
    this.csrfMatcher = csrfMatcher;
    this.csrfProcessor = csrfProcessor;
  }

  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                  FilterChain filterChain)
      throws ServletException, IOException {
    //从token仓库得到csrf
    CsrfToken csrfToken = this.tokenRepository.loadToken(request);

    //没有token新建
    boolean missingToken = csrfToken == null;
    if (missingToken) {
      csrfToken = this.tokenRepository.generateToken(request);
      this.tokenRepository.saveToken(csrfToken, request, response);
    }
    //token放入request中给页面使用
    request.setAttribute(csrfToken.getParameterName(), csrfToken);

    //如果是忽略掉的请求直接放行
    if (!csrfMatcher.matches(request)) {
      filterChain.doFilter(request, response);
      return;
    }

    //先从header中获取token,没有再从请求参数中获取
    String actualToken = request.getHeader(csrfToken.getHeaderName());
    if (actualToken == null) {
      actualToken = request.getParameter(csrfToken.getParameterName());
    }
    //token匹配放行,不匹配进行处理
    if (!csrfToken.getToken().equals(actualToken)) {
      csrfProcessor.process(request, response);
      return;
    }
    filterChain.doFilter(request, response);
  }

}
